home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / ip / ppp / mac / macppp1.1.3-src.hqx / MacPPP1.1.3-src / link.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-19  |  23.9 KB  |  888 lines

  1. /*
  2.  *  link.c    - routines to open/close serial link for PPP
  3.  *
  4.  * Copyright 1992-1993 Merit Network, Inc. and The Regents of the
  5.  *  University of Michigan.  Usage of this source code is restricted
  6.  *  to non-profit, non-commercial purposes.  The source is provided
  7.  *  "as-is", without warranty.
  8.  */
  9. #include "ppp.h"
  10. #include <Folders.h>
  11. #include <GestaltEqu.h>
  12. #include <CommResources.h>
  13. #include <CRMSerialDevices.h>
  14.  
  15. /* Trap numbers */
  16. #define TN_UnknownOS        0xA09F
  17. #define TN_CommToolbox        0xA08B
  18.  
  19. unsigned char PortBUse : 0x291;
  20.  
  21. /* function prototypes */
  22. OSErr waitstr(LapInfo *lap, char *, int);
  23. OSErr sendstr(unsigned char *);
  24. void install_tm(LapInfo *, struct TMprocess *, ProcPtr);
  25. void AppendStr(unsigned char *, unsigned char *);
  26. OSErr parsestr(unsigned char *, Boolean);
  27. OSErr TermMode();
  28. void sendlcpecho();
  29. void time_check();
  30. void MySetText(unsigned char);
  31. pascal Boolean nullfilt(DialogPtr, EventRecord *, int *);
  32.  
  33. /* define some globals */
  34. char **gmessages;
  35. Handle ghandle;
  36. char reserrmes[]="\pMacPPP: resource error";
  37. char okmes[]={1, 2,'O','K',0};
  38. extern struct NMRec errnmrec;
  39. char resFile[] = "\pPPP";
  40. char sys6resFile[] = "\p:System Folder:PPP";
  41.  
  42. /* resource defines */
  43. #define SYS6FOLDER -4034
  44. #define PREFFILENAME -4033
  45.  
  46. #define MODEMRESPONSES 128 /* resource id of modem responses */
  47. #define STATUSSTRINGS 129
  48. #define MODEMCOMMANDS 130
  49. #define PHASETEXT 131
  50.  
  51. #define MODEMCONNECT 1    /* # for connect message */
  52. #define MODEMBUSY 2        /* # for busy message */
  53.  
  54. #define MESSCHECK 1
  55. #define MESSINIT 2
  56. #define MESSDIAL 3
  57. #define MESSBUSY 4
  58. #define MESSERROR 5
  59. #define MESSSENDING 6
  60. #define MESSWAITING 7
  61. #define MESSTERM 8
  62. #define MESSTIMEOUT 9
  63. #define MESSPHASE 10
  64.  
  65. #define MODEMALERT    128        /* Modem connect failure alert */
  66. #define NOPREFALERT    129        /* no preferences alert */
  67. #define SERIALALERT 130        /* alert for error when opening serial port */
  68. #define TIMEOUTALERT 131    /* timeout waiting for a string */
  69. #define IDLEALERT    132
  70. #define ECHOALERT    133
  71. #define PAPMESALERT 134
  72. #define OLDPREFSALERT 135
  73. #define INUSEALERT    136        /* port in use alert */
  74.  
  75. /* dialogs */
  76. #define PPPSTATUS    129        /* status dialog */
  77. #define TERMDLOG    130        /* terminal window dialog box */
  78.  
  79. #define IGNOREIDLE 2
  80. #define IGNOREECHO    2
  81. #define CLOSEPPP    3
  82.  
  83. void
  84. link_open(register LapInfo *lap)
  85. {
  86. register OSErr        rc, savrc;
  87. OSErr        waitresult;
  88. SerShk        hshake;
  89. Boolean        HaveFindFolder = false, HavePrefFolder = false;
  90. long        ffresp, count, whichDir, oldA4, oldA5, timeout;
  91. Byte        i;
  92. int            whichVol, rn = -1, len, prefref;
  93. int            savresfile, itemhit, type;
  94. char        *strptr;
  95. register DialogPtr    statusdlog;
  96. DialogPtr    dlog;
  97. Rect        rect;
  98. Handle        itemH, itemH2;
  99. pascal        Boolean modemfilt();
  100. struct        bufheader *bufptr;
  101. b_8            curphase, curstate;
  102. char        **modemstrings;
  103. char        tempstr[MAXSLEN + 4];
  104. char        indriv[128],outdriv[128];
  105. long        qd_save;
  106.  
  107.     /* save current Resource file */
  108.     savresfile = CurResFile();
  109.     
  110.     oldA4 = seta4((long)lap->LapA4);    /* setup PPP's A4 for globals */
  111.     oldA5 = seta5((long) CurrentA5);
  112.     qd_save = *((long *)geta5());    /* save current pointer to QD globals */
  113.     InitGraf(&thePort);        /* init our QD globals */
  114.     InitFonts();            /* init fonts */
  115.     GetWMgrPort(&dlog);        /* check for window manager port */
  116.     if (dlog == nil) {    /* hopefully, these should be set up, but maybe not */
  117.         InitWindows();
  118.         if (TEScrpHandle ==  nil)
  119.             TEInit();
  120.         InitDialogs(nil);
  121.     }
  122.     InitCursor();    /* make the cursor an arrow */
  123.  
  124.     /* see if findfolder is available */
  125.     if ( Gestalt(gestaltFindFolderAttr, &ffresp) == noErr) {
  126.         if ( ( 1L << gestaltFindFolderPresent ) & ffresp )
  127.             HaveFindFolder = true;
  128.     }
  129.     
  130.     if ( HaveFindFolder ) {
  131.         if ( FindFolder(kOnSystemDisk,kExtensionFolderType,
  132.                 kDontCreateFolder,&whichVol,&whichDir) == noErr)
  133.             rn = HOpenResFile(whichVol,whichDir,resFile,fsRdPerm);
  134.     } else {
  135.         GetVRefNum(SysMap, &whichVol);    /* get system Vol ref */
  136.         rn = OpenRFPerm(sys6resFile,whichVol,fsRdPerm);
  137.     }
  138.     
  139.     if (rn == -1) {
  140.         errnmrec.nmStr = reserrmes;    /* initialize pointer to error message */
  141.         NMInstall(&errnmrec);        /* Install the notification */
  142.         goto noresbye;
  143.     }
  144.  
  145.     if (lap->ppp_flags & ECHO_FAIL) {
  146.         itemhit = NoteAlert(ECHOALERT, nil);
  147.         if (itemhit == IGNOREECHO) {
  148.             lap->echo_count = 0;
  149.             PrimeTime(&(lap->echo_task), (long)lap->prefdata.echo * 1000L);
  150.             goto exitout;
  151.         }
  152.         if (itemhit == CLOSEPPP)
  153.             goto close_ppp;
  154.         link_close(lap);        /* must want to restart */
  155.     }
  156.         
  157.     if (lap->ppp_flags & IDLE_TIMEOUT) {
  158.         if (!lap->prefdata.quiet) {
  159.             itemhit = NoteAlert(IDLEALERT, nil);
  160.             if (itemhit == IGNOREIDLE) {
  161.                  lap->idle_timer = 0;
  162.                 PrimeTime(&(lap->timeout_task), 60000L); /* one minute interval */
  163.                 goto exitout;
  164.             }
  165.         }
  166.         goto close_ppp;
  167.     }
  168.     
  169.     lap->ppp_flags &= ~(ECHO_FAIL | IDLE_TIMEOUT);
  170.  
  171.     if (lap->ppp_phase != pppDEAD)
  172.         return;        /* return if not in DEAD phase */
  173.  
  174.     if (HaveFindFolder) {
  175.         if (noErr == FindFolder(kOnSystemDisk,kPreferencesFolderType,
  176.                 kCreateFolder,&whichVol,&whichDir))
  177.             HavePrefFolder = true;
  178.     }
  179.  
  180.     tempstr[0] = 0;
  181.     if (!HavePrefFolder) {
  182.         itemH = GetResource('STR ',SYS6FOLDER); /* system 6 folder */
  183.         AppendStr(tempstr, *((unsigned char **) itemH));
  184.         ReleaseResource(itemH);
  185.     }
  186.     itemH = GetResource('STR ',PREFFILENAME); /* get pref file */
  187.     AppendStr(tempstr, *((unsigned char **) itemH));
  188.     ReleaseResource(itemH);
  189.  
  190.     if (HavePrefFolder)
  191.         rc = HOpen(whichVol,whichDir,tempstr,fsRdPerm,&prefref);
  192.     else {
  193.     /* if we don't have PrefFolder, just use System Folder */
  194.         GetVRefNum(SysMap, &whichVol);    /* get system Vol ref */
  195.         rc = FSOpen(tempstr,whichVol,&prefref);
  196.     }
  197.     savrc = rc;
  198.  
  199.     count = sizeof (struct ppp_pref);
  200.     if (rc == opWrErr )  /* must already be opened by control panel */
  201.         rc = SetFPos(prefref,fsFromStart,0L);
  202.     if (rc == noErr)
  203.         rc = FSRead(prefref, &count, &(lap->prefdata)); /* read in preferences data */
  204.  
  205.     if (lap->prefdata.version < PREF_VERSION) {
  206.         NoteAlert(OLDPREFSALERT,nil);
  207.         FSClose(prefref);
  208.         goto exitout;
  209.     }
  210.     if (rc == noErr) {
  211.         if    (count != sizeof (struct ppp_pref))  /* check number of bytes */
  212.             rc = -1;    /* set return code to non-zero */
  213.     }
  214.     if (rc == noErr)
  215.         rc = SetFPos(prefref, fsFromMark,
  216.                 lap->prefdata.active_config * sizeof(struct ppp_config));
  217.     if (rc == noErr) {
  218.             count = sizeof(struct ppp_config);
  219.             rc = FSRead(prefref, &count, &(lap->configdata));
  220.     }
  221.     if (savrc != opWrErr )
  222.         FSClose(prefref);        /* close the preferences file */
  223.  
  224.     if ( rc != noErr ) {
  225.         NoteAlert(NOPREFALERT, nil);
  226.         goto exitout;
  227.     }
  228.     indriv[0] = 0;
  229.     outdriv[0] = 0;
  230.     if (NGetTrapAddress(TN_CommToolbox, OSTrap)
  231.             != GetTrapAddress(TN_UnknownOS)) {
  232.         CRMRec *crmrecptr, acrmrec;
  233.         CRMSerialRecord *serrec;
  234.         long old = 0;
  235.         int index = 1;
  236.  
  237.         InitCRM();
  238.         acrmrec.qType = crmType;
  239.         while (true) {
  240.             acrmrec.crmDeviceType = crmSerialDevice;
  241.             acrmrec.crmDeviceID = old;
  242.             if (!(crmrecptr = (CRMRec *)CRMSearch((QElemPtr)(&acrmrec))))
  243.                 break;
  244.             serrec = (CRMSerialRecord *)crmrecptr->crmAttributes;
  245.             old = crmrecptr->crmDeviceID;
  246.             ++index;
  247.             if (EqualString(lap->prefdata.portname, *serrec->name,
  248.                     FALSE, TRUE))
  249.                 break;
  250.         }
  251.         if (!crmrecptr)
  252.             goto sererr;
  253.         AppendStr(indriv, *serrec->inputDriverName);
  254.         AppendStr(outdriv, *serrec->outputDriverName);
  255.  
  256.     } else {
  257.         if (!(itemH = GetNamedResource('Port',lap->prefdata.portname)))
  258.             goto sererr;
  259.         strptr = * ((char **) itemH);
  260.         AppendStr(indriv,strptr);
  261.         strptr += *strptr + 1;
  262.         AppendStr(outdriv,strptr);
  263.         ReleaseResource(itemH);
  264.     }
  265.     rc = OpenDriver(indriv, &lap->serinrefnum);
  266.     if (rc == portInUse) {
  267.         if (EqualString(indriv,"\p.BIn", FALSE, FALSE)) {
  268.             if (NoteAlert(INUSEALERT, nil) == OK) {
  269.                 PortBUse = (Byte) 0xff;        /* Hack to mark port as free */
  270.                 rc = OpenDriver(indriv, &lap->serinrefnum);
  271.             }
  272.         }
  273.     }
  274.     if (rc == noErr) {
  275.         rc = OpenDriver(outdriv, &lap->seroutrefnum);
  276.         if (rc != noErr)
  277.             CloseDriver(lap->serinrefnum);
  278.     }
  279.     
  280.     if (rc != noErr)
  281.         goto sererr;
  282.     rc = SerReset(lap->serinrefnum,stop10+noParity+data8);
  283.     if (rc == noErr)
  284.         rc = SerReset(lap->seroutrefnum,stop10+noParity+data8);
  285.     if (rc == noErr)
  286.         rc = SerSetBuf(lap->serinrefnum, lap->sdinbuf, SDINBUFLEN);    /* give driver a new buffer */
  287.  
  288.     lap->stat_pb.csCode = 13;        /* baud rate routine */
  289.     *( (unsigned int *) lap->stat_pb.csParam) = lap->configdata.baudrate;
  290.     lap->stat_pb.ioCRefNum = lap->serinrefnum;
  291.     asm {
  292.         lea    lap->stat_pb,a0
  293.         _PBControl    IMMED
  294.     }
  295.     *( (unsigned int *) lap->stat_pb.csParam) = lap->configdata.baudrate;
  296.     lap->stat_pb.ioCRefNum = lap->seroutrefnum;
  297.     asm {
  298.         lea    lap->stat_pb,a0
  299.         _PBControl    IMMED
  300.     }
  301.  
  302.     hshake.fDTR = false;     /* don't bother with input flow ctl */
  303.     hshake.fCTS = lap->configdata.flowctl_on; /* enable/disable flow ctl */
  304.     hshake.errs = 0;    /* don't abort reads on errors */
  305.     hshake.evts = 0;    /* don't interrupt on events */
  306.     hshake.fXOn = false; /* no Xon/Xoff flow control */
  307.     hshake.fInX = false;
  308.     
  309.     if (rc == noErr)
  310.         rc = SerHShake(lap->serinrefnum,&hshake);
  311.     if (rc == noErr)
  312.         rc = SerHShake(lap->seroutrefnum,&hshake);
  313.     if (rc != noErr) {
  314.         SerSetBuf(lap->serinrefnum, lap->sdinbuf, 0); /* reset buffer */
  315.         CloseDriver(lap->serinrefnum);
  316.         CloseDriver(lap->seroutrefnum);
  317. sererr:
  318.         NoteAlert(SERIALALERT, nil);
  319.         goto exitout;
  320.     }
  321.     
  322.     /* Initialize lists of buffers */
  323.     lap->buflist = (struct bufheader * ) nil;
  324.     for ( i = 0; i < NUMBUFFERS; i++ ) {
  325.         bufptr = lap->blockarray[i].block;
  326.         bufptr->dataptr = (unsigned char *) lap->buflist;
  327.         lap->buflist = bufptr;
  328.     }
  329.     
  330.     lap->rds.rdsparm.lnb.lnb_ptr = nil;        /* clear rcv buffer pointer */
  331.  
  332.     lap->active = nil;
  333.     lap->out_q.qFlags = 0;
  334.     /* initialize queue of iopbs */
  335.     lap->pppbq.qFlags = 0;
  336.     lap->pppbq.qHead = nil;
  337.     lap->pppbq.qTail = nil;
  338.     for (i = 0; i < NUMIOPBS; i++)
  339.         Enqueue(&(lap->pppiopbs[i]), &(lap->pppbq));
  340.  
  341.     /* initialize FIFO params */
  342.     lap->XmitQHead = 0;
  343.     lap->XmitQTail = 0;
  344.     lap->XmitQSize = 0;
  345.     
  346.     /* initialize our serial driver iopb's */
  347.     lap->w_iopb.lap = lap;
  348.     lap->w_iopb.iop.ioCompletion = hdlcwioc;    /* where to go when done */
  349.     lap->w_iopb.iop.ioRefNum = lap->seroutrefnum;    /* set output to serial port*/
  350.     
  351.     lap->r_iopb.lap = lap;
  352.     lap->r_iopb.iop.ioRefNum = lap->serinrefnum;
  353.     lap->r_iopb.iop.ioBuffer = lap->rxbuf;
  354.     lap->r_iopb.iop.ioCompletion = SerReadDone;
  355.  
  356.     lap->stat_pb.ioCRefNum = lap->serinrefnum;
  357.     
  358.     lap->read_state = s_Init;
  359.     lap->write_state = s_Init;
  360.     lap->term_mode = false;    /* not in terminal emulation mode */
  361.     
  362.     lap->bufptr = getbuffer();    /* allocate initial rcv. buffer */
  363.     lap->rddata = lap->bufptr->dataptr;    /* point to data area */
  364.     lap->ok_to_xmit = true;
  365.  
  366.     SystemTask();        /* some serial drivers need SystemTask time to open */
  367.     Delay(60L, &count);    /* wait some (if DTR need to "wake up" device */
  368.     SystemTask();        /* more system task time for good measure */
  369.  
  370.     /* set up receive time manager task */
  371.     install_tm(lap, &(lap->rxp_task.atm), ProcRcvPPP);
  372.     PrimeTime(&(lap->rxp_task), 3L);    /* Prime receive task */
  373.  
  374.     /* set up transmit time manager task (gets primed as needed) */
  375.     install_tm(lap, &(lap->txp_task.atm), ProcXmtPPP);
  376.     
  377.     if (lap->prefdata.echo != 0)    /* LCP echo test */
  378.          install_tm(lap, &(lap->echo_task.atm), sendlcpecho);
  379.      if (lap->prefdata.timeout != 0)    /* idle timeout */
  380.          install_tm(lap, &(lap->timeout_task.atm), time_check);
  381.  
  382.     statusdlog = GetNewDialog(PPPSTATUS, nil, (WindowPtr) -1L);
  383.     DrawDialog(statusdlog);
  384.     SetCursor(&qd.arrow);
  385.     GetDItem(statusdlog, 3, &type, &ghandle, &rect);
  386.     GetDItem(statusdlog, 4, &type, &itemH2, &rect);
  387.     SetIText(itemH2, "\p");    /* set to nil */
  388.     gmessages = Get1Resource('STR#', STATUSSTRINGS);
  389.     
  390.     if (lap->prefdata.use_term) {
  391.         if (TermMode() != noErr)
  392.             goto getout;
  393.         goto start_ppp;
  394.     }
  395.     
  396.     if (lap->configdata.phonenum[0] != 0) {
  397. retrymodem:
  398.         DrawDialog(statusdlog);
  399.         MySetText(MESSCHECK);
  400.         sendstr("\pATE0V1\r");        /* see if modem is there */
  401.         if ( (rc = waitstr(lap, okmes, 5))
  402.                     == 0) { /* wait for response */
  403.             goto retrymodem;
  404.         } else if ( rc == -1 )
  405.             goto getout;
  406.  
  407.         if ( i = lap->configdata.modeminit[0] ) {
  408.             MySetText(MESSINIT);
  409.             lap->configdata.modeminit[0] = 2;
  410.             if (! EqualString(lap->configdata.modeminit, "\pAT", FALSE, FALSE) )
  411.                 sendstr("\pAT");
  412.             lap->configdata.modeminit[0] = i;
  413.             sendstr(lap->configdata.modeminit); /* send modem init command */
  414.             sendstr("\p\r");        /* add a carriage return */
  415.             if ((rc = waitstr(lap, okmes, 5)) == 0) { /* wait for response */
  416.                 goto retrymodem;
  417.             } else if (rc == -1)
  418.                 goto getout;
  419.         }
  420.  
  421.         MySetText(MESSDIAL);
  422. doredial:
  423.         modemstrings = Get1Resource('STR#',MODEMRESPONSES);
  424.         DrawDialog(statusdlog);
  425.         if (lap->prefdata.use_pulse)
  426.             sendstr("\pATDP");
  427.         else
  428.             sendstr("\pATDT");
  429.         sendstr(lap->configdata.phonenum);
  430.         sendstr("\p\r");
  431.         HLock((Handle) modemstrings);
  432.         rc = waitstr(lap, *modemstrings + 1,
  433.                         lap->configdata.connecttimeout);
  434.         HUnlock((Handle) modemstrings);
  435.         ReleaseResource(modemstrings);
  436.         if (rc == MODEMBUSY) {
  437.             MySetText(MESSBUSY);
  438.             sendstr("\pAT\r");    /* get the modems' attention */
  439.             Delay(120L, &count);            /* add some delay before we redial */
  440.             goto doredial;
  441.         }
  442.         if (rc == 0)
  443.             goto retrymodem;
  444.         if (rc == -1)
  445.             goto getout;
  446.         if (rc != MODEMCONNECT) {
  447.             ResetAlrtStage();
  448.             if ( NoteAlert(MODEMALERT, nil) == 1 ) {    /* alert user */
  449.                 goto getout;
  450.             }
  451.             goto doredial;
  452.         }
  453.     }
  454.  
  455. retrystr:
  456.     DrawDialog(statusdlog);
  457.     for ( i=0 ; i < NUMCOMMANDS; i++) {
  458.         strptr = tempstr;
  459.         *strptr++ = 1;
  460.         *strptr = 0;
  461.         AppendStr(strptr, lap->configdata.commands[i].scriptstr);
  462.         if (lap->configdata.commands[i].addreturn)
  463.             AppendStr(strptr, "\p\\r");
  464.         if ( *strptr != 0 ) {
  465.             if ( lap->configdata.commands[i].sendout ) {
  466.                 MySetText(MESSSENDING);
  467.                 SetIText(itemH2, strptr);
  468.                 parsestr(strptr, true);
  469.             } else {
  470.                 MySetText(MESSWAITING);
  471.                 SetIText(itemH2, strptr);
  472.                 parsestr(strptr, false);
  473.                 if ((rc = waitstr(lap, tempstr,
  474.                         lap->configdata.waittimeout)) == 0)
  475.                     goto retrystr;
  476.                 else if ( rc == -1 )
  477.                     goto getout;
  478.             }
  479.         }
  480.     }
  481.  
  482. start_ppp:
  483.     
  484.     lap->write_state = s_Idle;        /* serial port now ready for PPP */
  485.     lap->read_state = s_Idle;
  486.  
  487.     ppp_iostatus(lap, PARAM_UP);    /* let PPP know that link is up */
  488.     
  489.     curphase = 255;        /* initialize state holder */
  490.  
  491.     MySetText(MESSPHASE);
  492.     ReleaseResource(gmessages);    /* release messages resource */
  493.     gmessages = Get1Resource('STR#', PHASETEXT);
  494.     ghandle = itemH2;
  495.     while (lap->ppp_fsm[IPcp].state != fsmOPENED) {    /* wait for IPCP */
  496.         ModalDialog(nullfilt, &itemhit);
  497.         if (itemhit == 1) {        /* check if user wants to quit */
  498.             goto getout;
  499.         }
  500.         if (lap->ppp_phase != curphase) {
  501.             curphase = lap->ppp_phase;
  502.             MySetText(curphase + 1);    /* display the PPP phase */
  503.         }
  504.         if (lap->pap_i.IOFlag == TRUE && lap->pap_i.message[0] == 0)
  505.             if (pap_userio(&(lap->ppp_fsm[Pap])) != noErr ) { /* get user name and password */
  506.                 goto getout;
  507.             }
  508.         if (lap->pap_i.message[0] != 0) {
  509.             if (!lap->prefdata.quiet || lap->pap_i.IOFlag == true) {
  510.                 ResetAlrtStage();
  511.                 ParamText(lap->pap_i.message, nil, nil, nil);
  512.                 Alert(PAPMESALERT, nil);    /* display PAP messages */
  513.             }
  514.             lap->pap_i.message[0] = 0;    /* reset the message */
  515.         }
  516.     }
  517.  
  518.     DisposDialog(statusdlog);
  519.      if (lap->prefdata.echo != 0) {
  520.          lap->echo_count = 0;
  521.         PrimeTime(&(lap->echo_task), (long)lap->prefdata.echo * 1000L);
  522.     }
  523.  
  524.      if (lap->prefdata.timeout != 0) {
  525.          lap->idle_timer = 0;
  526.         PrimeTime(&(lap->timeout_task), 60000L); /* one minute interval */
  527.     }
  528.     goto exitout;        /* everything okey-dokey */
  529.  
  530. getout:
  531.     DisposDialog(statusdlog);    /* get rid of status dialog */
  532.     ReleaseResource(gmessages);    /* release resource */
  533. close_ppp:
  534.     PPPClose(lap);        /* set fsm state to INITIAL for LCP and IPCP */
  535. exitout:
  536.     CloseResFile(rn);    /* make sure to close the resource file */
  537. noresbye:
  538.     *((long *)geta5()) = qd_save;    /* restore QD globals ptr */
  539.     seta4(oldA4);        /* restore previous value of A4 */
  540.     seta5(oldA5);
  541.     UseResFile(savresfile);    /* restore resource file */
  542. }
  543.  
  544. void
  545. link_close(register LapInfo *lap)
  546. {
  547.     int        i;
  548.     long    count;
  549.  
  550.     ppp_iostatus(lap, PARAM_DOWN);    /* link is going down */
  551.     if (lap->transProc != nil)
  552.         (*(lap->transProc))(TransitionClose);    /* let MacTCP know we are down */
  553.  
  554.      if (lap->prefdata.echo != 0)
  555.         RmvTime(&(lap->echo_task));
  556.      if (lap->prefdata.timeout != 0)
  557.         RmvTime(&(lap->timeout_task));
  558.     RmvTime(&(lap->rxp_task));
  559.     KillIO(lap->serinrefnum);
  560.     RmvTime(&(lap->txp_task));
  561.     KillIO(lap->seroutrefnum);        /* kill pending writes */
  562.     SerSetBuf(lap->serinrefnum, lap->sdinbuf, 0); /* reset buffer */
  563.  
  564.     if (lap->prefdata.hangup) {    /* hangup modem */
  565.         Delay(65L, &count);        /* wait ~1 second */
  566.         for (i = 0 ; i < 3 ; i++) {
  567.             sendstr("\p+");        /* send a '+' 3 times */
  568.             Delay(12L, &count);    /* wait 200 milliseconds */
  569.         }
  570.         Delay(65L, &count);        /* wait ~1 second */
  571.         sendstr("\pATH\r");        /* send the hang-up command */
  572.         Delay(30L, &count);        /* wait another 1/2 second */
  573.     }
  574.  
  575.     CloseDriver(lap->serinrefnum);
  576.     CloseDriver(lap->seroutrefnum);
  577. }
  578.  
  579. void
  580. MySetText(unsigned char stringnum)
  581. {
  582. register unsigned char *strptr;
  583.  
  584.     strptr = *gmessages;
  585.     strptr++;
  586.     if (stringnum > *strptr++ )        /* check if string is out of range */
  587.         return;
  588.     while (--stringnum > 0)            /* find string in list */
  589.         strptr += *strptr + 1;
  590.     HLock((Handle) gmessages);        /* lock down the Handle */
  591.     SetIText(ghandle, strptr);    /* set the text in the dialog */
  592.     HUnlock((Handle) gmessages);
  593. }
  594.  
  595. pascal Boolean modemfilt(DialogPtr dlog, EventRecord *evt, int *item)
  596. {
  597. char        c;
  598. ioParam        iopb;
  599. LapInfo        *lap;
  600.  
  601.     lap = GetLAPPtr();
  602.     *item = 0;
  603.     if (evt->what == keyDown) {
  604.         c = evt->message & charCodeMask;
  605.         iopb.ioRefNum = lap->seroutrefnum;
  606.         iopb.ioBuffer = &c;        /* set data pointer */
  607.         iopb.ioReqCount = 1;        /* number of bytes to send */
  608.         PBWrite(&iopb, false);            
  609.         *item = 3;
  610.     } else if (evt->what == mouseDown)
  611.         return FALSE;
  612.         
  613.     return TRUE;
  614. }
  615.  
  616. pascal Boolean nullfilt(DialogPtr dlog, EventRecord *evt, int *item)
  617. {
  618.     *item = 0;
  619.     if (evt->what == mouseDown)
  620.         return false;
  621.     return true;
  622. }
  623.  
  624. OSErr parsestr(unsigned char *instr, Boolean sendout)
  625. {
  626. OSErr            rc;
  627. long            count;
  628. register char    *outptr;
  629. register char    *inptr;
  630. unsigned char    inlen;
  631. register char    c;
  632.  
  633.     outptr = inptr = instr;
  634.     if ( (inlen = (*outptr++ = *inptr++) ) == 0 )
  635.         return;
  636.     while ( inlen-- != 0 ) {
  637.         if ( ( c = *inptr++ ) == '^' ) {
  638.             if ( inlen-- == 0)        /*  and check for 0 */
  639.                 break;
  640.             c = *inptr++ & 0x1F;    /* generate the control char */
  641.             *outptr++ = c;
  642.         } else if (c == '\\') {
  643.             c = *inptr++;
  644.             if ( inlen-- == 0)
  645.                 break;
  646.             switch (c) {
  647.             case '^':            /* literals */
  648.             case '\\':
  649.                 *outptr++ = c;
  650.                 break;
  651.             case 'r':
  652.                 *outptr++ = 13;        /* carriage return */
  653.                 break;
  654.             case 'b':
  655.             case 'd':
  656.             case 't':
  657.                 if (!sendout)        /* only allow on outgoing strings */
  658.                     break;
  659.                 *instr = (unsigned char) (outptr - instr - 1);
  660.                 sendstr(instr);        /* send the current string */
  661.                 instr = outptr - 1;    /* point to current place in string */
  662.                 if ( c == 'd' )
  663.                     Delay(60L, &count);        /* Delay 1 second */
  664.                 else if (c == 'b') {
  665.                     LapInfo *lap;
  666.                     
  667.                     lap = GetLAPPtr();        /* get our Lap Pointer */
  668.                     SerSetBrk(lap->seroutrefnum);    /* put line in breaking state */
  669.                     Delay(6L, &count);                /* wait a huner' milliseconds */
  670.                     SerClrBrk(lap->seroutrefnum);    /* clear break condition */
  671.                 } else {
  672.                     if ( (rc = TermMode()) != noErr)
  673.                         return rc;
  674.                 }
  675.                 break;
  676.             default:
  677.                 if  ( c >= '0' && c <= '7' ) {
  678.                     *outptr = 0;
  679.                     count = 3;
  680.                     do {
  681.                         *outptr <<= 3;
  682.                         *outptr += c - '0';
  683.                         c = *inptr++;
  684.                     } while ( c >= '0' && c <= '7' && --count != 0 && inlen-- != 0);
  685.                     inptr--;    /* point back to charcter */
  686.                     outptr++;
  687.                     if (inlen == 0xff)
  688.                         inlen = 0;
  689.                 }
  690.                 break;
  691.             }
  692.         } else {
  693.             *outptr++ = c;
  694.         }
  695.     }
  696.     *instr = (unsigned char) (outptr - instr - 1); /* store len byte */
  697.     if (sendout)
  698.         sendstr(instr);        /* send the string */
  699. }
  700.  
  701.  /* simple terminal window routine */
  702.  
  703. OSErr TermMode()
  704. {
  705. LapInfo     *lap;
  706. TEHandle    teH;
  707. Rect        rect;
  708. register DialogPtr    dlog;
  709. Handle        itemH;
  710. int            type, len, nlines, itemhit;
  711. GrafPtr        oldport;
  712.     
  713.     lap = GetLAPPtr();
  714.     lap->term_mode = true;        /* in terminal emulation mode */
  715.     MySetText(MESSTERM);
  716.     dlog = GetNewDialog(TERMDLOG, nil, (WindowPtr) -1L);
  717.     GetPort(&oldport);
  718.     SetPort(dlog);
  719.     DrawDialog(dlog);    /* draw the dialog so title shows up */
  720.  
  721.     GetDItem(dlog, 3, &type, &itemH, &rect);
  722.     TextFont(courier);
  723.     TextSize(10);
  724.  
  725.     teH = TENew(&rect, &rect);    
  726.     len = 0;
  727.     nlines = 10;    
  728.     FrameRect(&rect);
  729.  
  730.     do {
  731.         ModalDialog(modemfilt, &itemhit);
  732.  
  733.         if (lap->bufptr->length != len) {
  734.             len = lap->bufptr->length;
  735.             EraseRect(&rect);
  736.             TESetText(lap->bufptr->dataptr, (long)len, teH);
  737.             TECalText(teH);
  738.             if ((**teH).nLines > nlines) {
  739.                 TEScroll(0, (**teH).lineHeight * -1 * ((**teH).nLines - nlines), teH);
  740.                 nlines = (**teH).nLines;
  741.             }
  742.             TEUpdate(&rect, teH);
  743.             FrameRect(&rect);
  744.         }
  745.     } while (itemhit != OK && itemhit != Cancel);
  746.         
  747.     lap->term_mode = false;
  748.     DisposeDialog(dlog);    /* Dispose the Dialog */
  749.     SetPort(oldport);
  750.     if (itemhit == Cancel)
  751.         return -1;
  752.     return noErr;
  753. }
  754.  
  755. /* routine to send out a string.  may want to add timeout code. */
  756.  
  757. OSErr sendstr(unsigned char *s)
  758. {
  759. ioParam iopb;
  760. LapInfo    *lap;
  761.  
  762.     if (*s == 0)
  763.         return;                /* check for null string */
  764.     lap = GetLAPPtr();
  765.     iopb.ioRefNum = lap->seroutrefnum;
  766.     iopb.ioReqCount = *s++;    /* get length */
  767.     iopb.ioBuffer = s;        /* set buffer pointer */
  768.     return (PBWrite(&iopb,false));    /* call synchronously */
  769. }
  770.  
  771. void 
  772. adjustbuf(LapInfo *lap, int i)
  773. {
  774. long    savsr;
  775. struct    bufheader *bufptr = lap->bufptr;
  776.     
  777.     savsr = set_sr(0x2100);    /* disable timer ints, so we don't buffer more */
  778.     bufptr->length -= i;        /* subtract from count */
  779.     lap->rddata -= i;        /* adjust pointer */
  780.     BlockMove(bufptr->dataptr + i, bufptr->dataptr, bufptr->length);    /*move down buffer */
  781.     set_sr(savsr);        /* restore timer interrupts */
  782. }
  783.  
  784. OSErr waitstr(LapInfo *lap, char *s, int waitseconds )
  785. {
  786. long    timeout;
  787. register int    i;
  788. int        itemhit;
  789. unsigned char    len, numstrs, j;
  790. char    *lenptr;
  791.  
  792.     timeout = TickCount() + waitseconds*60;    /* seconds before timing out */
  793.     
  794.     for (i=1; true; i++) {
  795.         ModalDialog(nullfilt, &itemhit);
  796.         if (itemhit == 1) {        /* check if user wants to quit */
  797.             return -1;
  798.         }
  799.                     
  800.         if (TickCount() > timeout) {        /* don't sit in for loop forever */
  801.             ResetAlrtStage();
  802.             ParamText(s + 1,nil,nil,nil);
  803.             if ( NoteAlert(TIMEOUTALERT, nil) == 1 ) {    /* alert user */
  804.                 return -1;
  805.             }
  806.             return 0;                /* user would like to retry */
  807.         }
  808.             
  809.         if (lap->bufptr->length < i) {    /* check if any chars came in */
  810.             --i;
  811.             continue;
  812.         }
  813.         
  814.         if (i > 255) {        /* don't let buffer get too full */
  815.             adjustbuf(lap, 128);
  816.             i -= 128;
  817.         }
  818.  
  819.         lenptr = s;        /* point to number of string field */
  820.         numstrs = *lenptr++;    /* get the number of strings */
  821.         j = 0;
  822.         while ( j < numstrs ) {
  823.             j++;
  824.             if ((len = *lenptr++) == 0 )
  825.                 continue;
  826.             if (i >= len) {            /* do we have enough to do a compare? */
  827.                 if ( bytecmp(lap->bufptr->dataptr + i - len, lenptr, len)
  828.                          == 0) {    /* compare and exit if equal */
  829.                     adjustbuf(lap, i);    /* shift the data in the buffer */
  830.                     return j;            /* return success */
  831.                 }
  832.             }
  833.             lenptr += len;        /* point to next string */
  834.         }
  835.     }
  836. }
  837.  
  838. void sendlcpecho() {
  839.     register LapInfo    *lap;
  840.     struct TMprocess    *tmprocp;
  841.  
  842.     asm {
  843.         move.l    a1,tmprocp
  844.     }
  845.  
  846.     lap = tmprocp->tmsavptr.lap;
  847.     if (lap->echo_count++ >= lap->prefdata.echo_tries) {
  848.         if (!(lap->ppp_flags & IDLE_TIMEOUT) && lap->transProc != nil) {
  849.             lap->ppp_flags |= ECHO_FAIL;
  850.             (*(lap->transProc))(TransitionClose);    
  851.             (*(lap->transProc))(TransitionOpen);    /* a hack to get system time */
  852.             return;
  853.         } else
  854.             lap->echo_count = 0;
  855.     }
  856.      /* send a LCP echo request */
  857.     lap->lcp_echo_buf.header.dataptr = lap->lcp_echo_buf.buffer
  858.             + LCP_ECHO_BUFSIZE;        /* initialize dataptr */
  859.     lap->lcp_echo_buf.header.length = 0;    /* initialize count */
  860.     fsm_send(&(lap->ppp_fsm[Lcp]), ECHO_REQ, 0, &(lap->lcp_echo_buf));
  861. }
  862.  
  863. void time_check() {
  864.     register LapInfo    *lap;
  865.     struct TMprocess    *tmprocp;
  866.  
  867.     asm {
  868.         move.l    a1,tmprocp
  869.     }
  870.  
  871.     lap = tmprocp->tmsavptr.lap;
  872.     if (lap->idle_timer++ >= lap->prefdata.timeout) {
  873.         if (!(lap->ppp_flags & ECHO_FAIL) && lap->transProc != nil) {
  874.             lap->ppp_flags |= IDLE_TIMEOUT;
  875.             (*(lap->transProc))(TransitionClose);    
  876.             (*(lap->transProc))(TransitionOpen);    /* a hack to get system time */
  877.         } else
  878.             lap->idle_timer = 0;        /* reset idle timer */
  879.     }
  880.     PrimeTime(&(lap->timeout_task), 60000L);    /* re-prime for 1 minute */
  881. }
  882.  
  883. void install_tm (LapInfo *lap, register struct TMprocess *tmptr, ProcPtr task) {
  884.     bzero((char *) tmptr, (short) sizeof(TMTask));
  885.     tmptr->atm.tmAddr = task;
  886.     tmptr->tmsavptr.lap = lap;
  887.     InsTime(tmptr);
  888. }